package com.lahuan.config.datasouce;

import com.lahuan.config.datasouce.entity.*;

import javax.sql.DataSource;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lfy
 */
public class CustomDataSourceProxyUtil {
    /**
     * 代理数据源，在获取链接的时候，转换成xa连接
     *
     * @param ds
     * @return
     */
    public static DataSource proxyDatasource(DataSource ds, String dsName) {
        return (DataSource) Proxy.newProxyInstance(Object.class.getClassLoader(),
                new Class[]{DataSource.class}, (proxy, method, args) -> {
                    String name = method.getName();
                    //System.out.println(dsName + ":" + name);
                    if ("getConnection".equals(name)) {
                        return proxyConnection((Connection) method.invoke(ds, args), ds, dsName);
                    }
                    return method.invoke(ds, args);
                });
    }

    public static Connection proxyConnection(Connection connection, DataSource ds, String dsName) {

        return (Connection) Proxy.newProxyInstance(Object.class.getClassLoader(),
                new Class[]{Connection.class}, (proxy, method, args) -> {
                    String name = method.getName();
                    //System.out.println(dsName + "-connection:" + name);
                    if ("createStatement".equals(name)) {
                        //Statement preparedStatement = connection.prepareStatement(" SELECT 1 ",Statement.RETURN_GENERATED_KEYS);

                        return proxyStatement((Statement) method.invoke(connection, args), ds, dsName);
                    }
                    return method.invoke(connection, args);
                });
    }

    public static Statement proxyStatement(Statement statement, DataSource ds, String dsName) {

        return (Statement) Proxy.newProxyInstance(Object.class.getClassLoader(),
                new Class[]{Statement.class}, (proxy, method, args) -> {
                    String name = method.getName();
                    //System.out.println(dsName + "-statement:" + name);
                    CustomSqlParserResult parserResult = null;
                    if ("execute".equals(name) && CustomTransactionHolder.getGlobalTransactionState() == GlobalTransactionState.exec){

                        String sql = (String) args[0];
                        //将状态修改为解析状态，因为可能要用相同的数据源连数据库做一些，避免死循环
                        CustomTransactionHolder.setGlobalTransactionState(GlobalTransactionState.execParse);
                        //解析sql
                        System.out.println("正在解析并执行sql,"+dsName+":" + sql);
                        parserResult = SqlParserUtil.parseResult(sql);
                        //删除和修改需要在记录执行之前的信息
                        if (parserResult instanceof UpdateCustomSqlParserResult) {
                            UpdateCustomSqlParserResult res = (UpdateCustomSqlParserResult) parserResult;
                            String tableName = res.getTableName();
                            ColumnValuePair idColumnValue = res.getIdColumnValue();
                            List<ColumnValuePair> beforeColumnValue = getBeforeValue(tableName, idColumnValue, ds);
                            res.setBeforeColumnValue(beforeColumnValue);
                            res.setDataSource(ds);
                            res.setDataSourceName(dsName);
                        }
                        if (parserResult instanceof DeleteCustomSqlParserResult) {
                            DeleteCustomSqlParserResult res = (DeleteCustomSqlParserResult) parserResult;
                            String tableName = res.getTableName();
                            ColumnValuePair idColumnValue = res.getIdColumnValue();
                            List<ColumnValuePair> beforeColumnValue = getBeforeValue(tableName, idColumnValue, ds);
                            res.setBeforeColumnValue(beforeColumnValue);
                            res.setDataSource(ds);
                            res.setDataSourceName(dsName);
                        }
                        //解析完成后
                        CustomTransactionHolder.setGlobalTransactionState(GlobalTransactionState.exec);


                        if (parserResult instanceof InsertCustomSqlParserResult) {
                            InsertCustomSqlParserResult res =  (InsertCustomSqlParserResult) parserResult;
                            res.setDataSource(ds);
                            res.setDataSourceName(dsName);
                            Object result = executeInsert(ds, res, sql);
                            //System.out.println("解析后:"+parserResult);
                            //将当前解析结果记录进事务分支中
                            CustomTransactionHolder.getCurrentTransactionBranch().getCustomSqlParserResult().add(parserResult);
                            return result;
                        }else{
                            //System.out.println("解析后:"+parserResult);
                            if(parserResult !=null){
                                //将当前解析结果记录进事务分支中
                                CustomTransactionHolder.getCurrentTransactionBranch().getCustomSqlParserResult().add(parserResult);
                            }
                            return method.invoke(statement, args);
                        }


                    }

                    return method.invoke(statement, args);
                });
    }

    private static Object executeInsert(DataSource ds, InsertCustomSqlParserResult parserResult, String sql) throws SQLException {
        Connection connection = ds.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        Object result = preparedStatement.execute();
        ResultSet rs = preparedStatement.getGeneratedKeys();

        if(rs.next()){

            String pkColumnName = getPrimaryKeyName(ds,parserResult);

            Object idValue = rs.getObject(1);
            ColumnValuePair idColumnValue = new ColumnValuePair();
            idColumnValue.setColumn( pkColumnName);
            idColumnValue.setValue(idValue);
            parserResult.setIdColumnValue(idColumnValue);

        }
        preparedStatement.close();
        connection.close();
        return result;
    }

    private static String getPrimaryKeyName(DataSource ds, InsertCustomSqlParserResult parserResult) throws SQLException {
        try {
            Connection conn = ds.getConnection();
            String tableName = parserResult.getTableName();
            DatabaseMetaData dmd = conn.getMetaData();
            tableName=tableName.replaceAll("`","");
            ResultSet rs = dmd.getPrimaryKeys(null, conn.getSchema(), tableName);
            rs.next();
            String primaryKey = rs.getString("column_name");
            conn.close();
            return primaryKey;
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return "id";
    }

    private static List<ColumnValuePair> getBeforeValue(String tableName, ColumnValuePair idColumnValue, DataSource ds) throws SQLException {

        String idColumn = idColumnValue.getColumn();
        Object idValue = idColumnValue.getValue();
        String sqlIdValue ;
        if (idValue instanceof String) {
            sqlIdValue = "'" + idValue + "'";
        } else {
            sqlIdValue = idValue.toString();
        }

        String querySql = "select * from " + tableName + " where " + idColumn + " = " + sqlIdValue;
        Connection connection = ds.getConnection();
        Statement statement = connection.createStatement();
        ResultSet rs = statement.executeQuery(querySql);
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        if( rs.next()){
            List<ColumnValuePair> before = new ArrayList<>();
            for (int i = 1; i <=columnCount; i++) {
                String columnName = metaData.getColumnName(i);
                String columnValue ;
                String className = metaData.getColumnClassName(i);
                if(className.equals(String.class.getName())){
                    String strValue = rs.getString(columnName);

                    if(strValue==null){
                        columnValue=null;
                    }else{
                        //这里字符串类型直接用引号包起来，需要生成反向sql的时候可以直接用
                        columnValue = "'"+strValue+"'";
                    }

                }else{
                    Object val = rs.getObject(columnName);
                    //目前只支持的字符串和数字类型
                    if(val==null){
                        columnValue=null;
                    }else{
                        columnValue = rs.getObject(columnName).toString();
                    }
                }
                ColumnValuePair pair = new ColumnValuePair();
                pair.setColumn(columnName);
                pair.setValue(columnValue);
                before.add(pair);
            }
            return before;
        }
        return null;
    }


}
